﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace MinhUtils.Threading
{
    /// <summary>
    /// A safe Queue that can be access concurrently
    /// </summary>
    public class ConcurrentQueue<T>
    {
        private Queue<T> queue;

        /// <summary>
        /// Initialize a safe Queue that can be access concurrently
        /// </summary>
        public ConcurrentQueue()
        {
            queue = new Queue<T>();
        }

        /// <summary>
        /// Return number of object in the queue
        /// </summary>
        public int Size
        {
            get
            {
                int size = 0;
                lock (queue)
                {
                    size = queue.Count();
                }

                return size;
            }
        }

        /// <summary>
        /// Safely add an item to the queue, and notify all waiting threads.
        /// </summary>
        public void Enqueue(T item)
        {
            lock (queue)
            {
                queue.Enqueue(item);
                Monitor.PulseAll(queue);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        public T Dequeue()
        {
            T value = default(T);
            lock (queue)
            {
                value = queue.Dequeue();
                Monitor.PulseAll(queue);
            }

            return value;
        }

        /// <summary>
        /// Attempts to safely removes and returns the object at the beginning of the queue. If there is no object in the queue, wait until being notified and try again util getting one.
        /// </summary>
        public T TryDequeue()
        {
            T value = default(T);

            lock (queue)
            {
                while (queue.Count == 0)
                    Monitor.Wait(queue);

                value = queue.Dequeue();
                Monitor.PulseAll(queue);
            }

            return value;
        }

        /// <summary>
        /// Attempts to safely removes and returns the object at the beginning of the queue. If there is no object in the queue, wait for an interval or until being notified then try again until getting one.
        /// </summary>
        /// <param name="interval">The number of milliseconds to wait after each try</param>
        public T TryDequeue(int interval)
        {
            T value = default(T);

            lock (queue)
            {
                while (queue.Count == 0)
                    Monitor.Wait(queue, interval);

                value = queue.Dequeue();
                Monitor.PulseAll(queue);
            }

            return value;
        }
    }
}
